From a216b9f7cd7a259a19d1672a621db993e9a5c88e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 15 Aug 2014 18:15:13 -0700 Subject: [PATCH] Preserve $OUT_DIR across rebuilds The original choice for completely destroying $OUT_DIR was to focus on repeatable builds to ensure that stale items in $OUT_DIR don't affect later compilations. This commit moves this responsibility from cargo to the build command itself. Build commands are now responsible for cleaning out old artifacts and ensuring that when invoked the state of $OUT_DIR is as it would be if it started with an empty $OUT_DIR. This will allow for very long build commands based on `make` to have a much faster incremental build time as they won't have to rebuild everything when updated. This can cause non-repeatable builds with build commands if stale artifacts are never removed (but still used in the rust compilation step), but this will now be considered a bug in the build command rather than for cargo itself. Closes #382 --- src/cargo/ops/cargo_rustc/mod.rs | 7 ++++- tests/test_cargo_compile.rs | 51 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 1bf38eac4..0bb383d6b 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -164,6 +164,7 @@ fn compile_custom(pkg: &Package, cmd: &str, // may be building a C lib for a plugin let layout = cx.layout(KindTarget); let output = layout.native(pkg); + let old_output = layout.proxy().old_native(pkg); let mut p = process(cmd.next().unwrap(), pkg, cx) .env("OUT_DIR", Some(&output)) .env("DEPS_DIR", Some(&output)) @@ -173,7 +174,11 @@ fn compile_custom(pkg: &Package, cmd: &str, } Ok(proc() { if first { - try!(fs::mkdir(&output, UserRWX).chain_error(|| { + try!(if old_output.exists() { + fs::rename(&old_output, &output) + } else { + fs::mkdir(&output, UserRWX) + }.chain_error(|| { internal("failed to create output directory for build command") })); } diff --git a/tests/test_cargo_compile.rs b/tests/test_cargo_compile.rs index c5834741f..9f916ea41 100644 --- a/tests/test_cargo_compile.rs +++ b/tests/test_cargo_compile.rs @@ -1525,3 +1525,54 @@ test!(freshness_ignores_excluded { {fresh} foo v0.0.0 ({url}) ", fresh = FRESH, url = foo.url()))); }) + +test!(rebuild_preserves_out_dir { + let mut build = project("builder"); + build = build + .file("Cargo.toml", r#" + [package] + name = "build" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("src/main.rs", r#" + use std::os; + use std::io::File; + + fn main() {{ + let path = Path::new(os::getenv("OUT_DIR").unwrap()).join("foo"); + if os::getenv("FIRST").is_some() { + File::create(&path).unwrap(); + } else { + File::create(&path).unwrap(); + } + }} + "#); + assert_that(build.cargo_process("cargo-build"), execs().with_status(0)); + + let foo = project("foo") + .file("Cargo.toml", format!(r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = '{}' + "#, build.bin("build").display()).as_slice()) + .file("src/lib.rs", "pub fn bar() -> int { 1 }"); + foo.build(); + foo.root().move_into_the_past().assert(); + + assert_that(foo.process(cargo_dir().join("cargo-build")) + .env("FIRST", Some("1")), + execs().with_status(0) + .with_stdout(format!("\ +{compiling} foo v0.0.0 ({url}) +", compiling = COMPILING, url = foo.url()))); + + File::create(&foo.root().join("src/bar.rs")).assert(); + assert_that(foo.process(cargo_dir().join("cargo-build")), + execs().with_status(0) + .with_stdout(format!("\ +{compiling} foo v0.0.0 ({url}) +", compiling = COMPILING, url = foo.url()))); +}) -- 2.30.2